package com.fubowen.reader.book.impl

import com.fubowen.reader.book.BookChapterBean
import com.fubowen.reader.book.IBook
import com.fubowen.reader.db.bean.WebSiteBean
import com.fubowen.reader.extension.AGENT
import com.fubowen.reader.extension.toNumber
import com.fubowen.reader.extension.favicon
import com.fubowen.reader.extension.fetchOrThrow
import com.fubowen.reader.extension.getDocument
import com.fubowen.reader.extension.trimChar
import com.fubowen.reader.db.model.toTreeItem
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.net.URL

class WebBook(override var url: String, override var onCompleted: IBook.() -> Unit = {}) : IBook {
    override var isGetCatalogState = false
    override val chapterList = mutableListOf<BookChapterBean>()
    override var name: String = "未解析"
    override var errMsg: String? = null
    override var onProgressChanged: (BookChapterBean) -> Unit = {}

    private val tryCount: Int = 3
    private val timeout: Int = 30000
    private var isCatalogOfInitUrl = false
    private var document: Document? = null
    private val chapterRegex = "第\\s*[一二三四五六七八九十零\\d]+\\s*章"
    var onDownloadProgressChanged: (Int) -> Unit = {}
    var onDownloadCompleted: () -> Unit = {}
    var isDownloadChapter = false

    override fun tryGet() {
        var document = fetchOrThrow(url)
        var aList = document.select("a[href]").toList()
        var chapterNodeList = aList.getChapterList()
        if (chapterNodeList.isEmpty()) {
            val catalogUrl = aList.getCatalogUrl()
            check(catalogUrl.isNotEmpty()) { "没有获得书籍目录" }
            for (it in catalogUrl) {
                document = fetchOrThrow(it)
            }
        }
        aList = document.select("a[href]").toList()
        chapterNodeList = aList.getChapterList()
        check(chapterNodeList.isNotEmpty()) { "没有获得章节列表" }

    }

    private fun List<Element>.getChapterList(): List<Element> {
        val regex = Regex("第\\s*[一二三四五六七八九十零\\d]+\\s*章(.*)$")
        return this.filter { regex.containsMatchIn(it.text()) }
    }

    private fun List<Element>.transform(): MutableList<Chapter> {
        val chapterList = mutableListOf<Chapter>()
        val regex = Regex("第\\s*([一二三四五六七八九十零\\d])+\\s*章(.*)$")
        for (item in this) {
            val matchResult = regex.find(item.text())
            val href = item.attr("href")
            val url = URL(URL(item.baseUri()), href).toExternalForm()
            if (matchResult != null) {
                val bean = Chapter(
                    name = matchResult.groupValues[2].trimChar() ?: "",
                    order = matchResult.groupValues[1].toNumber(),
                    orderOld = matchResult.groupValues[1],
                    url = url
                )
                chapterList.add(bean)
            }
        }
        return chapterList
    }

    private fun List<Element>.getCatalogUrl(): List<String> {
        val regex = Regex("目录|章节列表|返回目录|在线阅读")
        return this.filter { regex.matches(it.text()) }.map { URL(URL(it.baseUri()), it.attr("href")).toExternalForm() }
    }

    fun content(url: String): String {
        val document = fetchOrThrow(url)
        val node = document.selectFirst(":has(>br:gt(10))")
        return node?.wholeText()?.replace("\r", "\n") ?: ""
    }

    data class Chapter(
        val order: Int = 0,
        val orderOld: String = "",
        var name: String = "",
        var url: String = ""
    )

    private fun tryGetChapterList(): Boolean {
        return document?.let {
            val nodeList = it.select("a:matches($chapterRegex)").distinct()
            val regex = Regex("第\\s*[一二三四五六七八九十零\\d]+\\s*章(.*)$")
            for (i in nodeList.indices) {
                val item = nodeList[i]
                val matchResult = regex.find(item.text())
                val href = item.attr("href")
                val absoluteUrl = URL(URL(item.baseUri()), href)
                val url = absoluteUrl.toExternalForm()
                if (matchResult != null) {
                    val bean = BookChapterBean(
                        name = matchResult.groupValues[1].trimChar() ?: "",
                        order = i + 1,
                        url = url,
                        isDownload = false
                    )
                    chapterList.add(bean)
                }
            }
            nodeList.isNotEmpty()
        } ?: false
    }

    override fun tryGetCatalog() {
        if (tryGetChapterList()) {
            isGetCatalogState = true
            tryGetName()
            tryGetUrl()
            isCatalogOfInitUrl = true
        } else {
            document?.let {
                val regex = "目录|章节列表|返回目录"
                val nodes = it.select("a:matches($regex)").distinct()
                for (item in nodes) {
                    val href = item.attr("href")
                    val absoluteUrl = URL(URL(item.baseUri()), href)
                    val url = absoluteUrl.toExternalForm()
                    document = Jsoup.connect(url).timeout(timeout).userAgent(AGENT).get()
                    if (tryGetChapterList()) {
                        isGetCatalogState = true
                        tryGetName()
                        tryGetUrl()
                        break
                    }
                }
            }
        }
    }

    override fun tryGetName() {
        document?.let {
            var meta = it.select("meta[property*=book_name]")
            if (meta != null) {
                name = meta.attr("content")
                return
            }
            meta = it.select("meta[property*=title]")
            if (meta != null) {
                name = meta.attr("content")
            }
        }
    }

    override fun tryGetUrl() {
        url = document?.baseUri() ?: ""
    }

    companion object {
        @JvmStatic
        fun getChapterContent(url: String?, callback: (String) -> Unit) {
            url.getDocument { state, doc, _ ->
                if (state && doc != null) {
                    val node = doc.selectFirst(":has(>br:gt(10))")
                    if (node != null) {
                        node.text()
                        val content = node.wholeText().replace("\r", "\n")
                        callback(content)
                    }
                }
            }
        }

        fun webSite(url: String) = fetchOrThrow(url).run {
            val title = title()
            val keywords = selectFirst("meta[name='keywords']")?.attr("content")
            val description = selectFirst("meta[name='description']")?.attr("content")
            val favicon = favicon(url)
            val webSite = WebSiteBean(title, description, url, keywords, favicon)
            webSite.children += select("a[href]").map { it.toTreeItem() }
            webSite
        }
    }
}